package society.framework.android;

import java.lang.reflect.Constructor;
import java.util.HashMap;

import society.framework.core.BaseAutomaton;
import society.framework.core.SocietyManager;
import society.framework.core.communication.MessageRouter;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * Class which manages a society of automatons in the Android activity. It performs adding/removing/starting/stopping
 * automatons and persistence operations. Persistence operations deal with the SharedPreferences of the application.
 * @author Micu Andrei
 *
 */
public class AndroidSocietyManager extends SocietyManager{
	protected static final String PREFERENCES_FILE_NAME = "JOATPreferences.txt";
	protected static final String AUTOMATON_NAMES_TAG = "joat.automatonNames";
	protected static final String SUBSCRIBER_AUTOMATON_NAMES_TAG = "joat.subscribedAutomatonNames";
	protected static final String AUTOMATONS_TAG = "joat.automatons.";
	protected static final String AUTOMATON_STATE_TAG = ".state";
	protected static final String AUTOMATON_CLASS_TAG = ".class";
	
	protected Activity activity;					//the activity of the application
	protected SharedPreferences sharedPreferences;
	
	public AndroidSocietyManager(Activity activity){
		super();
		this.activity = activity;
		this.sharedPreferences = activity.getApplicationContext().getSharedPreferences(PREFERENCES_FILE_NAME, Context.MODE_PRIVATE);
	}

	/**
	 * Saves the state of the society in the SharedPreferences.<br/>
	 * Overwrite this method if you want to use another way of saving the society state.
	 * @return true if saving was successful or false otherwise
	 */
	public boolean saveState() {
		Editor editor = sharedPreferences.edit();
		//save the state, class and name for each automaton
		StringBuilder automatonNames = new StringBuilder();
		for (HashMap.Entry<String, BaseAutomaton> entry : automatons.entrySet()) {
		    if(!saveAutomaton(entry.getKey(), entry.getValue(), editor)){
		    	return false;
		    }
		    automatonNames.append(entry.getKey()).append(',');
		}
		if(automatonNames.length()>0){
			automatonNames.deleteCharAt(automatonNames.length()-1);
		}
		editor.putString(AUTOMATON_NAMES_TAG, automatonNames.toString());
		
		//also save the names of the automatons that were subscribed to the MessageRouter
		StringBuilder subscribedAutomatonNames = new StringBuilder();
		for(String name : MessageRouter.getInstance().getSubscriberNames()){
			subscribedAutomatonNames.append(name).append(',');
		}
		if(subscribedAutomatonNames.length()>0){
			subscribedAutomatonNames.deleteCharAt(automatonNames.length()-1);
		}
		editor.putString(SUBSCRIBER_AUTOMATON_NAMES_TAG, subscribedAutomatonNames.toString());
		return editor.commit();
	}
	
	/**
	 * Method which saves an automaton to the SharedPreferences (class and state representation).<br/>
	 * Overwrite this method if you want to change the way an automaton is saved.
	 * @param name - name with which the automaton is identified
	 * @param automaton - reference to the automaton
	 * @param editor - an editor with which modifications to the preferences can be made
	 * @return - true if operation succeeded
	 */
	protected boolean saveAutomaton(String name, BaseAutomaton automaton, Editor editor){
		if(name==null){
			return false;
		}
		String representation = automaton.serializeToString();
		if(representation==null){
			return false;
		}
		editor.putString(AUTOMATONS_TAG + name + AUTOMATON_CLASS_TAG, automaton.getClass().getName());
		editor.putString(AUTOMATONS_TAG + name + AUTOMATON_STATE_TAG, representation);
		return true;
	}

	/**
	 * Loads the state of the society from the SharedPreferences.
	 * Overwrite this method if you want to use another way of loading the society state.
	 * @return - true if loading was successful or false otherwise
	 */
	public boolean loadState() {
		String names = sharedPreferences.getString(AUTOMATON_NAMES_TAG, null);
		//load all the automatons with the name in the preferences
		if(names!=null){
			String[] namesArray = names.split(",");
			BaseAutomaton automaton;
			for(int i=0; i<namesArray.length; i++){
				automaton = loadAutomaton(namesArray[i]);
				if(automaton==null){
					return false;
				} else {
					automatons.put(namesArray[i], automaton);
				}
			}
		}
		
		//then subscribe each automaton that was subscribed when the state was last saved
		String subscribedNames = sharedPreferences.getString(SUBSCRIBER_AUTOMATON_NAMES_TAG, null);
		if(subscribedNames!=null){
			String[] subscribedNamesArray = subscribedNames.split(",");
			BaseAutomaton automaton;
			for(String subscribedName : subscribedNamesArray){
				automaton = automatons.get(subscribedName);
				if(automaton==null){
					return false;
				}
				MessageRouter.getInstance().subscribe(subscribedName, automaton);
			}
		}
		
		return true;
	}
	
	/**
	 * Method which loads an automaton from the SharedPreferences.<br/>
	 * Overwrite this method if you want to change the way an automaton is loaded.
	 * @param name - name with which the automaton is identified
	 * @return - reference to the new automaton or null if operation failed
	 */
	protected BaseAutomaton loadAutomaton(String name){
		if(name==null) return null;
		
		String className = sharedPreferences.getString(AUTOMATONS_TAG + name + AUTOMATON_CLASS_TAG, null);
		
		BaseAutomaton automaton;
		try {
			Class<?> automatonClass = Class.forName(className);
			if(ActivityAutomaton.class.isAssignableFrom(automatonClass)){	//if the class is the same or subclass of ActivityAutomaton
				Constructor<?> constructor = automatonClass.getConstructor(Activity.class);
				automaton = (BaseAutomaton) constructor.newInstance(activity);
			} else if(BaseAutomaton.class.isAssignableFrom(automatonClass)){	//if the class is the same or subclass of Automaton
				automaton = (BaseAutomaton) automatonClass.newInstance();
			} else {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
		String representation = sharedPreferences.getString(AUTOMATONS_TAG + name + AUTOMATON_STATE_TAG, null);
		
		automaton.deserializeFromString(representation);
		
		return automaton;
	}
	
}
